package com.photonstorm.flixel
{
	import flash.events.MouseEvent;
	import org.flixel.*;
	
	/**
	 * A simple button class that calls a function when clicked by the mouse.
	 * Supports labels, highlight states, and parallax scrolling.
	 */
	public class FlxButtonPlus extends FlxGroup
	{
		/**
		 * Set this to true if you want this button to function even while the game is paused.
		 */
		public var pauseProof:Boolean;
		/**
		 * Used for checkbox-style behavior.
		 */
		protected var _onToggle:Boolean;
		/**
		 * Stores the 'off' or normal button state graphic.
		 */
		protected var _off:FlxSprite;
		/**
		 * Stores the 'on' or highlighted button state graphic.
		 */
		protected var _on:FlxSprite;
		/**
		 * Stores the 'off' or normal button state label.
		 */
		protected var _offT:FlxText;
		/**
		 * Stores the 'on' or highlighted button state label.
		 */
		protected var _onT:FlxText;
		/**
		 * This function is called when the button is clicked.
		 */
		protected var _callback:Function;
		/**
		 * Tracks whether or not the button is currently pressed.
		 */
		protected var _pressed:Boolean;
		/**
		 * Whether or not the button has initialized itself yet.
		 */
		protected var _initialized:Boolean;
		/**
		 * Helper variable for correcting its members' <code>scrollFactor</code> objects.
		 */
		protected var _sf:FlxPoint;
		
		/**
		 * The parameters passed to the Callback function when the button is clicked
		 */
		private var callbackParams:Array;
		
		/**
		 * Tracks whether or not the button is currently hovered over by the mouse
		 */
		protected var _hovered:Boolean;
		
		/**
		 * This function is called when the button is hovered over
		 */
		private var enterCallback:Function;
		
		/**
		 * The parameters passed to the enterCallback function when the button is hovered over
		 */
		private var enterCallbackParams:Array;
		
		/**
		 * This function is called when the mouse leaves a hovered button (but didn't click)
		 */
		private var leaveCallback:Function;
		
		/**
		 * The parameters passed to the leaveCallback function when the hovered button is left
		 */
		private var leaveCallbackParams:Array;
		
		/**
		 * The 1px thick border color that is drawn around this button
		 */
		public var borderColor:int = 0xffffffff;
		
		/**
		 * The color gradient of the button in its in-active (not hovered over) state
		 */
		public var offColor:Array = [0x008000, 0x00FF00];
		
		/**
		 * The color gradient of the button in its hovered state
		 */
		public var onColor:Array = [0x800000, 0xFF0000];
		
		/**
		 * Creates a new <code>FlxButton</code> object with a gray background
		 * and a callback function on the UI thread.
		 * 
		 * @param	X			The X position of the button.
		 * @param	Y			The Y position of the button.
		 * @param	Callback	The function to call whenever the button is clicked.
		 * @param	Params		An optional array of parameters that will be passed to the Callback function
		 * @param	Text		Text to display on the button
		 * @param	Width		The width of the button.
		 * @param	Height		The height of the button.
		 */
		public function FlxButtonPlus(X:int, Y:int, Callback:Function, Params:Array = null, Text:String = null, Width:int = 100, Height:int = 20):void
		{
			super();
			x = X;
			y = Y;
			width = Width;
			height = Height;
			
			_off = new FlxSprite().createGraphic(width, height, borderColor);
			_off.draw(FlxGradient.createGradientFlxSprite(width - 2, height - 2, offColor), 1, 1);
			_off.solid = false;
			add(_off, true);
			
			_on = new FlxSprite().createGraphic(width, height, borderColor);
			_on.draw(FlxGradient.createGradientFlxSprite(width - 2, height - 2, onColor), 1, 1);
			_on.solid = false;
			add(_on, true);
			
			_offT = null;
			_onT = null;
			_callback = Callback;
			_onToggle = false;
			_pressed = false;
			_initialized = false;
			_sf = null;
			pauseProof = false;
			_hovered = false;
			
			if (Text)
			{
				_offT = new FlxText(0, 3, width, Text);
				_offT.shadow = 0x000001;
				_offT.alignment = "center";
				_onT = _offT;
				add(_offT, true);
				_offT.scrollFactor = scrollFactor;
				_onT.scrollFactor = scrollFactor;
			}
			
			if (Params)
			{
				callbackParams = Params;
			}
		}
		
		/**
		 * If you want to change the color of this button in its in-active (not hovered over) state, then pass a new array of color values
		 * 
		 * @param	colors
		 */
		public function updateInactiveButtonColors(colors:Array):void
		{
			offColor = colors;
			
			_off.draw(FlxGradient.createGradientFlxSprite(width - 2, height - 2, offColor), 1, 1);
		}
		
		public function updateActiveButtonColors(colors:Array):void
		{
			onColor = colors;
			
			_on.draw(FlxGradient.createGradientFlxSprite(width - 2, height - 2, onColor), 1, 1);
		}
		
		/**
		 * If this button has text, set this to change the value
		 */
		public function set text(value:String):void
		{
			if (_offT)
			{
				_offT.text = value;
				_onT.text = value;
			}
		}
		
		/**
		 * Center this button (on the X axis) Uses FlxG.width / 2 - button width / 2 to achieve this.<br />
		 * Doesn't take into consideration scrolling
		 */
		public function screenCenter():void
		{
			x = (FlxG.width / 2) - (width / 2);
		}
		
		public function setMouseOverCallback(callback:Function, params:Array = null):void
		{
			enterCallback = callback;
			
			enterCallbackParams = params;
		}
		
		public function setMouseOutCallback(callback:Function, params:Array = null):void
		{
			leaveCallback = callback;
			
			leaveCallbackParams = params;
		}
		
		/**
		 * Set your own image as the button background.
		 * 
		 * @param	Image				A FlxSprite object to use for the button background.
		 * @param	ImageHighlight		A FlxSprite object to use for the button background when highlighted (optional).
		 * 
		 * @return	This FlxButton instance (nice for chaining stuff together, if you're into that).
		 */
		public function loadGraphic(Image:FlxSprite,ImageHighlight:FlxSprite=null):FlxButtonPlus
		{
			_off = replace(_off,Image) as FlxSprite;
			if(ImageHighlight == null)
			{
				if(_on != _off)
					remove(_on);
				_on = _off;
			}
			else
				_on = replace(_on,ImageHighlight) as FlxSprite;
			_on.solid = _off.solid = false;
			_off.scrollFactor = scrollFactor;
			_on.scrollFactor = scrollFactor;
			width = _off.width;
			height = _off.height;
			refreshHulls();
			return this;
		}

		/**
		 * Add a text label to the button.
		 * 
		 * @param	Text				A FlxText object to use to display text on this button (optional).
		 * @param	TextHighlight		A FlxText object that is used when the button is highlighted (optional).
		 * 
		 * @return	This FlxButton instance (nice for chaining stuff together, if you're into that).
		 */
		public function loadText(Text:FlxText,TextHighlight:FlxText=null):FlxButtonPlus
		{
			if(Text != null)
			{
				if(_offT == null)
				{
					_offT = Text;
					add(_offT);
				}
				else
					_offT = replace(_offT,Text) as FlxText;
			}
			if(TextHighlight == null)
				_onT = _offT;
			else
			{
				if(_onT == null)
				{
					_onT = TextHighlight;
					add(_onT);
				}
				else
					_onT = replace(_onT,TextHighlight) as FlxText;
			}
			_offT.scrollFactor = scrollFactor;
			_onT.scrollFactor = scrollFactor;
			return this;
		}
		
		/**
		 * Called by the game loop automatically, handles mouseover and click detection.
		 */
		override public function update():void
		{
			if(!_initialized)
			{
				if(FlxG.stage != null)
				{
					FlxG.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
					_initialized = true;
				}
			}
			
			super.update();

			visibility(false);
			
			if (overlapsPoint(FlxG.mouse.x, FlxG.mouse.y))
			{
				_pressed = FlxG.mouse.pressed();
				
				//	If they are over the button, but have not pressed the mouse, and this is the first time over the button, we fire the enter function (if any)
				if (_hovered == false && _pressed == false)
				{
					_hovered = true;
					
					if (enterCallback is Function)
					{
						enterCallback.apply(null, enterCallbackParams);
					}
				}
				
				visibility(!_pressed);
			}
			else
			{
				//	Not over the button
				if (_hovered)
				{
					//	It was just over the button, so reset that
					_hovered = false;
					
					if (leaveCallback is Function)
					{
						leaveCallback.apply(null, leaveCallbackParams);
						
					}
				}
			}
			
			if (_onToggle)
			{
				visibility(_off.visible);
			}
		}
		
		/**
		 * Use this to toggle checkbox-style behavior.
		 */
		public function get on():Boolean
		{
			return _onToggle;
		}
		
		/**
		 * @private
		 */
		public function set on(On:Boolean):void
		{
			_onToggle = On;
		}
		
		/**
		 * Called by the game state when state is changed (if this object belongs to the state)
		 */
		override public function destroy():void
		{
			if(FlxG.stage != null)
				FlxG.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
		}
		
		/**
		 * Internal function for handling the visibility of the off and on graphics.
		 * 
		 * @param	On		Whether the button should be on or off.
		 */
		protected function visibility(On:Boolean):void
		{
			if(On)
			{
				_off.visible = false;
				if(_offT != null) _offT.visible = false;
				_on.visible = true;
				if(_onT != null) _onT.visible = true;
			}
			else
			{
				_on.visible = false;
				if(_onT != null) _onT.visible = false;
				_off.visible = true;
				if(_offT != null) _offT.visible = true;
			}
		}
		
		/**
		 * Internal function for handling the actual callback call (for UI thread dependent calls like <code>FlxU.openURL()</code>).
		 */
		protected function onMouseUp(event:MouseEvent):void
		{
			if(!exists || !visible || !active || !FlxG.mouse.justReleased() || (FlxG.pause && !pauseProof) || (_callback == null)) return;
			
			if (overlapsPoint(FlxG.mouse.x, FlxG.mouse.y))
			{
				_callback.apply(null, callbackParams);
			}
		}
	}
}
